package com.pjb.springbootjjwt.store.server;

import com.pjb.springbootjjwt.store.Defines;
import com.pjb.springbootjjwt.store.File;
import com.pjb.springbootjjwt.store.Function;
import com.pjb.springbootjjwt.store.Sql;
import com.pjb.springbootjjwt.store.entity.Error;
import com.pjb.springbootjjwt.store.entity.*;
import org.apache.commons.lang3.ObjectUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Query {
    public static Query getInstance() {
        return new Query();
    }

    public Record select(AST.Tree tree, Record record) throws Exception {
        List<Clause> clause = Sql.getInstance().getClause(tree.getValue());
        getList(record, clause);
        List<Line> list = record.getLine();
        if (!list.isEmpty()) {

            List<Integer> select = Sql.getInstance().getSelectField(tree.getValue(), record.getHead());
            if (select == null) throw new Exception("" + Response.error.set(Error.E90004, "@" + tree.getId()));
            for (Line line : list) {
                filter(select, line);
            }
            Head head = Head.toHeadByAlias(tree.getValue());
            record.setHead(head);
        }

        return record;
    }

    public Record call(String sql) throws Exception {
        String table = sql.split("\\s+")[1];
        return Defines.getHandlerByEngine("procedure").get(table);
    }

    public Record fun(String sql) throws Exception {
        String table = sql.split("\\s+")[1];
        return Defines.getHandlerByEngine("fun").get(table);
    }

    /**
     * 查询入口
     *
     * @param sql
     * @return
     * @throws Exception
     */
    public Record select(String sql) throws Exception {
        List<Clause> clause;
        List<Table> tables;
        if (sql.contains("join")) {
            clause = Sql.getInstance().getJoinClause(sql);
            tables = Sql.getInstance().getJoinFrom(sql);
        } else {
            clause = Sql.getInstance().getClause(sql);
            tables = Sql.getInstance().getFrom(sql);
        }

        Record record;

        if (tables.isEmpty()) {
            record = Function.get(sql);
        } else if (tables.size() > 1) {
            record = getList(tables, clause);
        } else {
            record = getList(tables.get(0), clause);
        }
        List<Line> list = record.getLine();
        if (!list.isEmpty()) {
            List<Integer> select = Sql.getInstance().getSelectField(sql);
            if (select == null) throw new Exception("" + Response.error.set(Error.E90004, tables.toString()));
            for (Line line : list) {
                filter(select, line);
            }
        }
        if (!sql.contains("*")) {
            Head head = Head.toHeadByAlias(sql);
            record.setHead(head);
        }
        if (!sql.contains("from")) {
            Head head = Head.toHead(sql.trim().replaceAll("select", "").trim());
            record.setHead(head);
        }
        return record;
    }

    /**
     * 过滤字段
     *
     * @param fieldsIndex
     * @param line
     */
    private void filter(List<Integer> fieldsIndex, Line line) {
        if (fieldsIndex.isEmpty()) return;

        List<String> data = line.getData();
        List<String> filter = new ArrayList<>();
        for (Integer i : fieldsIndex) {
            filter.add(data.get(i));
        }
        line.setData(filter);
    }

    /**
     * 单表查询并根据条件过滤行记录
     *
     * @param table
     * @param clauses
     * @return
     * @throws IOException
     */
    public Record getList(Table table, List<Clause> clauses) throws Exception {
        String tableName = table.getTable();
        String ddl = File.getInstance().getDDL(tableName);
        String engine = Defines.getEngine(ddl);
        Head head = Head.toHeadByDDL(ddl);
        Record record = Defines.getHandler(tableName).get(tableName);//加载数据到内存
        List<Line> list = record.getLine();
        if (engine.equals("view")) {
            head = record.getHead();
        }

        if (!ObjectUtils.isEmpty(clauses))
            for (Clause c : clauses) {
                int index = Sql.getInstance().getFieldIndexByTable(table, c.getKey());
                list = where(list, c, index);//根据条件过滤
            }

        return Record.builder().head(head).line(list).build();
    }

    public Record getList(Record record, List<Clause> clauses) throws IOException {
        List<Line> list = record.getLine();
        if (!ObjectUtils.isEmpty(clauses))
            for (Clause c : clauses) {
                int index = record.getHead().getIndex(c.getKey());//Sql.getInstance().getFieldIndexByTable(table, c.getKey());
                list = where(list, c, index);//根据条件过滤
            }

        return Record.builder().head(record.getHead()).line(list).build();
    }

    /**
     * 多表联合查询
     *
     * @param tables
     * @param clauses
     * @return
     * @throws IOException
     */
    private Record getList(List<Table> tables, List<Clause> clauses) throws Exception {
        List<List<Line>> list = new ArrayList<>();
        Head head = new Head();
        if (!tables.isEmpty())
            for (Table table : tables) {
                String tableName = table.getTable();
                list.add(Defines.getHandler(tableName).get(tableName).getLine());
            }

        List<Line> r = QueryUtil.join(list);
        if (!ObjectUtils.isEmpty(clauses))
            for (Clause c : clauses) {
                int initcolumn = 0;
                for (Table table : tables) {
                    if (c.getKey().contains(table.getAlias()) || c.getValue().contains(table.getAlias())) {
                        int index = Sql.getInstance().getFieldIndexByTable(table, c.getKey()) + initcolumn;
                        r = where(r, c, index);//根据条件过滤
                    }
                    initcolumn += Sql.getInstance().getFields(table).size();//表共有多少个字段
                }
            }
        return Record.builder().head(head).line(r).build();
    }

    /**
     * 过滤记录
     *
     * @param list
     * @param clause
     * @param index
     * @return
     * @throws IOException
     */
    private List where(List<Line> list, Clause clause, int index) throws IOException {
        List result = new ArrayList();
        String op = clause.getOp();
        for (Line line : list) {
            String v = line.getData().get(index);
            String c = clause.getValue();
            if (op.equals("=") && Compare.eq(v, c)) {
                result.add(line);
                continue;
            }
            if ((op.equals("<>") || op.equals("!=")) && !Compare.eq(v, c)) {
                result.add(line);
                continue;
            }
            if (op.equals(">") && Compare.gt(v, c)) {
                result.add(line);
                continue;
            }
            if (op.equals("<") && Compare.lt(v, c)) {
                result.add(line);
                continue;
            }
            if (op.equals(">=") && Compare.gte(v, c)) {
                result.add(line);
                continue;
            }
            if (op.equals("<=") && Compare.lte(v, c)) {
                result.add(line);
            }
        }
        return result;
    }


}
